#include "memory_top.h"

void memory_top::entry(){
  socket.register_b_transport(this, &memory_top::b_transport);
  socket.register_get_direct_mem_ptr(this, &memory_top::get_direct_mem_ptr);
  socket.register_transport_dbg(this, &memory_top::transport_dbg);
  
  for(int i = 0; i < SIZE; i++) {
    mem[i] = 0xAA000000 | (rand() % SIZE);
    //mem[i] = 0xAA000000 | (i + 1);
    //cout << "i = " << i << ", mem[i] = " << hex << mem[i] << endl;
  }
  
  LATENCY = sc_time(5, SC_NS);
  SC_THREAD(invalidation_process);
}

void memory_top::b_transport(tlm::tlm_generic_payload& trans, sc_time& delay){
  tlm::tlm_command  cmd = trans.get_command();
  sc_dt::uint64     adr = trans.get_address() / 4; //address是int i, mem每一项是4B, 所以取址要除4, 本demo里默认读4B
  unsigned char *   ptr = trans.get_data_ptr();
  unsigned int      len = trans.get_data_length();
  unsigned char *   byt = trans.get_byte_enable_ptr();
  unsigned int      wid = trans.get_streaming_width();

  //cout << "adr = " << adr << endl;

  if (adr >= sc_dt::uint64(SIZE)) {
    trans.set_response_status(tlm::TLM_ADDRESS_ERROR_RESPONSE);
    return;
  }

  if (byt != 0) {
    trans.set_response_status(tlm::TLM_BYTE_ENABLE_ERROR_RESPONSE);
    return;
  }

  if (len > 4 || wid < len) {
    trans.set_response_status(tlm::TLM_BURST_ERROR_RESPONSE);
    return;
  }

  if (cmd == tlm::TLM_READ_COMMAND) {
    memcpy(ptr, &mem[adr], len);
  } else if (cmd == tlm::TLM_WRITE_COMMAND) {
    memcpy(&mem[adr], ptr, len);
  }

  wait(delay);
  delay = SC_ZERO_TIME;

  trans.set_dmi_allowed(true);
  trans.set_response_status(tlm::TLM_OK_RESPONSE);
}

bool memory_top::get_direct_mem_ptr(tlm::tlm_generic_payload& trans, tlm::tlm_dmi& dmi_data){
  dmi_data.allow_read_write();
  // ...
  dmi_data.set_dmi_ptr(reinterpret_cast<unsigned char*>( &mem[0] ));
  dmi_data.set_start_address(0);
  dmi_data.set_end_address(SIZE * 4 -1);
  dmi_data.set_read_latency(LATENCY);
  dmi_data.set_write_latency(LATENCY);

  return true;
}

unsigned int memory_top::transport_dbg(tlm::tlm_generic_payload& trans) {
  tlm::tlm_command  cmd = trans.get_command();
  sc_dt::uint64     adr = trans.get_address() / 4;
  unsigned char *   ptr = trans.get_data_ptr();
  unsigned int      len = trans.get_data_length();

  unsigned int num_bytes = (len < SIZE - adr) ? len : SIZE - adr;

  if (cmd == tlm::TLM_READ_COMMAND) {
    memcpy(ptr, &mem[adr], num_bytes);
  } else if (cmd == tlm::TLM_WRITE_COMMAND) {
    memcpy(&mem[adr], ptr, num_bytes);
  }

  return num_bytes;
}

void memory_top::invalidation_process() {
  for (int i = 0; i < 4; i ++) {
    wait(LATENCY * 8);
    socket->invalidate_direct_mem_ptr(0, SIZE - 1);
  }
}
